package main

import (
	"bufio"
	"flag"
	"fmt"
	"os"
	"runtime"
	"strconv"
	"strings"
	"time"
	"unicode"
)

type pulse struct {
    val  int         // 脉冲信号的值，通常包含控制标志或其他信息
    resp chan int    // 响应通道，用于发送确认或结果回传
}

// 初始化按钮和循环按钮通道，用于发送和接收整数信号
var initbut, cycbut chan int

// 用于发送字符串形式的打孔卡片数据
var ppunch chan string

// 各种开关（mpsw, conssw, cycsw, multsw, divsw, prsw）通道，用于发送和接收包含两个字符串的消息
var mpsw, conssw, cycsw, multsw, divsw, prsw chan [2]string

// accsw 数组，包含 20 个通道，每个通道用于发送和接收包含两个字符串的消息
var accsw [20]chan [2]string

// ftsw 数组，包含 3 个通道，每个通道用于发送和接收包含两个字符串的消息
var ftsw [3]chan [2]string

// 定义宽度和高度变量，可能用于图形界面或布局相关的设置
var width, height int

// 卡片扫描器，用于读取打孔卡片数据
var cardscanner *bufio.Scanner

// 打孔写入器，用于将数据写入打孔卡片
var punchwriter *bufio.Writer

// 演示模式、tkkludge 和 usecontrol 标志，用于控制程序的不同行为
var demomode, tkkludge, usecontrol *bool

// 将布尔值转换为字符串 "1" 或 "0"
func b2is(b bool) string {
    if b {
        return "1" // 如果布尔值为 true，返回 "1"
    }
    return "0" // 如果布尔值为 false，返回 "0"
}

// 将输入通道中的脉冲信号分发到多个输出通道
func fanout(in chan pulse, out []chan pulse) {
    var q pulse // 定义一个脉冲变量

    q.resp = make(chan int) // 初始化 q 的响应通道

    for {
        p := <-in // 从输入通道接收脉冲信号

        nresp := 0 // 记录已接收响应的数量

        if p.val != 0 { // 如果接收到的脉冲信号值不为 0
            q.val = p.val // 将脉冲信号值复制到 q 中

            // 遍历所有的输出通道
            for _, c := range out {
                foo:
                for {
                    select {
                    case c <- q: // 尝试将 q 发送到当前输出通道
                        break foo // 成功发送后跳出内层循环
                    case <-q.resp: // 如果从 q 的响应通道接收到响应
                        nresp++ // 增加已接收响应的数量
                    }
                }
            }
        }

        // 等待所有输出通道处理完消息
        for nresp < len(out) {
            <-q.resp // 接收响应直到所有输出通道都处理完消息
            nresp++
        }

        // 向原始脉冲信号的响应通道发送响应
        p.resp <- 1
    }
}

func proccmd(cmd string) int {
    // 将命令字符串按空格分割成多个字段
    f := strings.Fields(cmd)
    
    // 处理注释：如果字段中包含 "#" 开头的注释，则忽略该行后面的部分
    for i, s := range f {
        if s[0] == '#' {
            f = f[:i]
            break
        }
    }

    // 如果没有有效的字段，则返回 0
    if len(f) == 0 {
        return 0
    }
	/** 使用 strings.Fields 将命令字符串按空格分割为多个字段
	如果没有有效字段，则直接返回 0 **/

	/**
	根据第一个字段（命令类型）进行分支处理：
	b: 按钮操作，向特定通道发送整数值。
	d: 查询状态，根据不同的单元类型调用相应的状态查询函数。
	D: 打印所有状态信息。
	f: 文件操作，打开或创建文件，并设置卡片扫描器或打孔写入器。
	l: 加载程序，从文件读取并逐行处理命令。
	L: 设置 LED 矩阵支持的四个累加器。
	p: 跳线设置，根据提供的参数连接不同的组件
	**/

    // 根据第一个字段判断命令类型并执行相应操作
    switch f[0] {
    case "b":
        // 按钮操作
        if len(f) != 2 {
            fmt.Println("button syntax: b button")
            break
        }
        switch f[1] {
        case "c":
            initbut <- 5
        case "i":
            initbut <- 4
        case "p":
            cycbut <- 1
        case "r":
            initbut <- 3
        }

    case "d":
        // 状态查询
        if len(f) != 2 {
            fmt.Println("Status syntax: d unit")
            break
        }
        switch f[1][0] {
        case 'a':
            unit, _ := strconv.Atoi(f[1][1:])
            fmt.Println(accstat(unit - 1))
        case 'c':
            fmt.Println(consstat())
        case 'd':
            fmt.Println(divsrstat2())
        case 'f':
            unit, _ := strconv.Atoi(f[1][1:])
            fmt.Println(ftstat(unit - 1))
        case 'i':
            fmt.Println(initstat())
        case 'm':
            fmt.Println(multstat())
        case 'p':
            fmt.Println(mpstat())
        }

    case "D":
        // 打印所有状态信息
        fmt.Println(initstat())
        fmt.Println(mpstat())
        for i := 0; i < 20; i += 2 {
            fmt.Print(accstat(i))
            fmt.Print("   ")
            fmt.Println(accstat(i + 1))
        }
        fmt.Println(divsrstat2())
        fmt.Println(multstat())
        for i := 0; i < 3; i++ {
            fmt.Println(ftstat(i))
        }
        fmt.Println(consstat())

    case "f":
        // 文件操作
        if len(f) != 3 {
            fmt.Println("file syntax: f (r|p) filename")
            break
        }
        switch f[1] {
        case "r":
            fp, err := os.Open(f[2])
            if err != nil {
                fmt.Printf("Card reader open: %s\n", err)
                break
            }
            cardscanner = bufio.NewScanner(fp)
        case "p":
            fp, err := os.Create(f[2])
            if err != nil {
                fmt.Printf("Card punch open: %s\n", err)
                break
            }
            punchwriter = bufio.NewWriter(fp)
        }

    case "l":
        // 加载程序
        if len(f) != 2 {
            fmt.Println("Load syntax: l file")
            break
        }
        fd, err := os.Open(f[1])
        if err != nil {
            fd, err = os.Open("programs/" + f[1])
            if err != nil {
                fmt.Println(err)
                break
            }
        }
        sc := bufio.NewScanner(fd)
        for sc.Scan() {
            if proccmd(sc.Text()) < 0 {
                break
            }
        }
        fd.Close()

    case "L":
        // LED 矩阵支持
        if len(f) != 5 {
            fmt.Println("LED matrix support requires 4 accumulators")
            break
        }
        ledacc[0], _ = strconv.Atoi(f[1])
        ledacc[1], _ = strconv.Atoi(f[2])
        ledacc[2], _ = strconv.Atoi(f[3])
        ledacc[3], _ = strconv.Atoi(f[4])

    case "p":
        // 跳线设置
        if len(f) != 3 {
            fmt.Println("Invalid jumper spec", cmd)
            break
        }
        p1 := strings.Split(f[1], ".")
        p2 := strings.Split(f[2], ".")
        // 处理特殊跳线情况
        if len(p1) == 2 && p1[0][0] == 'a' && len(p1[1]) >= 2 &&
            (p1[1][:2] == "st" || p1[1][:2] == "su" ||
                p1[1][:2] == "il" || p1[1][:2] == "ir") {
            accinterconnect(p1, p2)
            break
        }
        ch := make(chan pulse)
        // 处理不同类型的跳线设置
        switch {
        case p1[0] == "ad":
            if len(p1) != 4 {
                fmt.Println("Adapter jumper syntax: ad.ilk.unit.param")
                break
            }
            unit, _ := strconv.Atoi(p1[2])
            param, _ := strconv.Atoi(p1[3])
            adplug(p1[1], 1, unit-1, param, ch)
        case p1[0][0] == 'a':
            if len(p1) != 2 {
                fmt.Println("Accumulator jumper syntax: aunit.terminal")
                break
            }
            unit, _ := strconv.Atoi(p1[0][1:])
            accplug(unit-1, p1[1], ch)
        case p1[0] == "c":
            if len(p1) != 2 {
                fmt.Println("Invalid constant jumper:", cmd)
                break
            }
            consplug(p1[1], ch)
        case p1[0] == "d":
            if len(p1) != 2 {
                fmt.Println("Divider jumper syntax: d.terminal")
                break
            }
            divsrplug(p1[1], ch)
        case p1[0][0] == 'f':
            if len(p1) != 2 {
                fmt.Println("Function table jumper syntax: funit.terminal")
                break
            }
            unit, _ := strconv.Atoi(p1[0][1:])
            ftplug(unit-1, p1[1], ch)
        case p1[0] == "i":
            if len(p1) != 2 {
                fmt.Println("Initiator jumper syntax: i.terminal")
                break
            }
            initplug(p1[1], ch)
        case p1[0] == "m":
            if len(p1) != 2 {
                fmt.Println("Multiplier jumper syntax: m.terminal")
                break
            }
            multplug(p1[1], ch)
        case p1[0] == "p":
            mpplug(p1[1], ch)
        case unicode.IsDigit(rune(p1[0][0])):
            hpos := strings.IndexByte(p1[0], '-')
            if hpos == -1 {
                tray, _ := strconv.Atoi(p1[0])
                if tray < 1 {
                    fmt.Println("Invalid data trunk", p1[0])
                    break
                }
                trunkrecv(0, tray-1, ch)
            } else {
                tray, _ := strconv.Atoi(p1[0][:hpos])
                line, _ := strconv.Atoi(p1[0][hpos+1:])
                trunkrecv(1, (tray-1)*11+line-1, ch)
            }
        default:
            fmt.Println("Invalid jack spec: ", p1)
        }
        // 同样处理第二个跳线端点
        switch {
        case p2[0] == "ad":
            if len(p2) != 4 {
                fmt.Println("Adapter jumper syntax: ad.ilk.unit.param")
                break
            }
            unit, _ := strconv.Atoi(p2[2])
            param, _ := strconv.Atoi(p2[3])
            adplug(p2[1], 0, unit-1, param, ch)
        case p2[0][0] == 'a':
            if len(p2) != 2 {
                fmt.Println("Accumulator jumper syntax: aunit.terminal")
                break
            }
            unit, _ := strconv.Atoi(p2[0][1:])
            accplug(unit-1, p2[1], ch)
        case p2[0] == "c":
            if len(p2) != 2 {
                fmt.Println("Invalid constant jumper:", cmd)
                break
            }
            consplug(p2[1], ch)
        case p2[0] == "d":
            if len(p2) != 2 {
                fmt.Println("Divider jumper syntax: d.terminal")
                break
            }
            divsrplug(p2[1], ch)
        case p2[0][0] == 'f':
            if len(p2) != 2 {
                fmt.Println("Function table jumper syntax: funit.terminal")
                break
            }
            unit, _ := strconv.Atoi(p2[0][1:])
            ftplug(unit-1, p2[1], ch)
        case p2[0] == "i":
            if len(p2) != 2 {
                fmt.Println("Initiator jumper syntax: i.terminal")
                break
            }
            initplug(p2[1], ch)
        case p2[0] == "m":
            if len(p2) != 2 {
                fmt.Println("Multiplier jumper syntax: m.terminal")
                break
            }
            multplug(p2[1], ch)
        case p2[0] == "p":
            mpplug(p2[1], ch)
        case unicode.IsDigit(rune(p2[0][0])):
            hpos := strings.IndexByte(p2[0], '-')
            if hpos == -1 {
                tray, _ := strconv.Atoi(p2[0])
                if tray < 1 {
                    fmt.Println("Invalid data trunk", p2[0])
                    break
                }
                trunkxmit(0, tray-1, ch)
            } else {
                tray, _ := strconv.Atoi(p2[0][:hpos])
                line, _ := strconv.Atoi(p2[0][hpos+1:])
                trunkxmit(1, (tray-1)*11+line-1, ch)
            }
        default:
            fmt.Println("Invalid jack spec: ", p2)
        }

    case "q":
        // 退出程序
        if guistate.proc != nil {
            guistate.proc.Kill()
        }
        if ledproc != nil {
            ledproc.Kill()
        }
        return -1

    case "r":
        // 重置操作
        if len(f) != 2 {
            fmt.Println("Status syntax: r unit")
            break
        }
        p := strings.Split(f[1], ".")
        switch p[0] {
        case "a":
            if len(p) != 2 {
                fmt.Println("Accumulator reset syntax: r a.unit")
            } else {
                unit, _ := strconv.Atoi(p[1])
                accreset(unit)
            }
        case "c":
            consreset()
        case "d":
            divreset()
        case "f":
            if len(p) != 2 {
                fmt.Println("Function table reset syntax: r f.unit")
            } else {
                unit, _ := strconv.Atoi(p[1])
                ftreset(unit)
            }
        case "i":
            initreset()
        case "m":
            multreset()
        case "p":
            mpreset()
        }

    case "R":
        // 全部重置
        initreset()
        cycreset()
        mpreset()
        ftreset(0)
        ftreset(1)
        ftreset(2)
        for i := 0; i < 20; i++ {
            accreset(i)
        }
        divreset()
        multreset()
        consreset()
        prreset()
        adreset()
        trayreset()

    case "s":
        // 设置开关
        if len(f) < 3 {
            fmt.Println("No switch setting")
            break
        }
        p := strings.Split(f[1], ".")
        switch {
        case p[0][0] == 'a':
            if len(p) != 2 {
                fmt.Println("Invalid accumulator switch:", cmd)
            } else {
                unit, _ := strconv.Atoi(p[0][1:])
                accsw[unit-1] <- [2]string{p[1], f[2]}
            }
        case p[0] == "c":
            if len(p) != 2 {
                fmt.Println("Constant switch syntax: s c.switch value")
            } else {
                conssw <- [2]string{p[1], f[2]}
            }
        case p[0] == "cy":
            if len(p) != 2 {
                fmt.Println("Cycling switch syntax: s cy.switch value")
            } else {
                cycsw <- [2]string{p[1], f[2]}
            }
        case p[0] == "d" || p[0] == "ds":
            if len(p) != 2 {
                fmt.Println("Divider switch syntax: s d.switch value")
            } else {
                divsw <- [2]string{p[1], f[2]}
            }
        case p[0][0] == 'f':
            if len(p) != 2 {
                fmt.Println("Function table switch syntax: s funit.switch value", cmd)
            } else {
                unit, _ := strconv.Atoi(p[0][1:])
                ftsw[unit-1] <- [2]string{p[1], f[2]}
            }
        case p[0] == "m":
            if len(p) != 2 {
                fmt.Println("Multiplier switch syntax: s m.switch value")
            } else {
                multsw <- [2]string{p[1], f[2]}
            }
        case p[0] == "p":
            if len(p) != 2 {
                fmt.Println("Programmer switch syntax: s p.switch value")
            } else {
                mpsw <- [2]string{p[1], f[2]}
            }
        case p[0] == "pr":
            if len(p) != 2 {
                fmt.Println("Printer switch syntax: s pr.switch value")
            } else {
                prsw <- [2]string{p[1], f[2]}
            }
        default:
            fmt.Printf("unknown unit for switch: %s\n", p[0])
        }

    case "u":
        // 占位符，未定义具体功能

    case "dt":
        // 占位符，未定义具体功能

    case "pt":
        // 占位符，未定义具体功能

    default:
        fmt.Printf("Unknown command: %s\n", cmd)
    }
    return 0
}

//
//  This code assumes that we're running on a Raspberry Pi
// with either Plan9 or Linux.  We also assume that the necessary
// exports have already been done for Linux.
//
func ctlstation() {
    var fdg, fd5, fd6, fd13, fd19, fd26, fd21, fd20 *os.File
    var host int
    var buf []byte
    var err error

    // 根据操作系统类型进行初始化
    if runtime.GOOS == "plan9" {
        host = 1
        fdg, err = os.OpenFile("#G/gpio", os.O_RDWR, 0)
        if err != nil {
            fmt.Println(err)
            return
        }
        _, err = fdg.WriteString("function 5 in\n")
        if err != nil {
            fmt.Println("Setting 5 to input", err)
        }
        fdg.WriteString("pullup 5\n")
        fdg.WriteString("function 6 in\n")
        fdg.WriteString("pullup 6\n")
        fdg.WriteString("function 13 in\n")
        fdg.WriteString("pullup 13\n")
        fdg.WriteString("function 19 in\n")
        fdg.WriteString("pullup 19\n")
        fdg.WriteString("function 20 in\n")
        fdg.WriteString("pullup 20\n")
        fdg.WriteString("function 21 in\n")
        fdg.WriteString("pullup 21\n")
        fdg.WriteString("function 26 in\n")
        fdg.WriteString("pullup 26\n")
        buf = make([]byte, 16)
    } else if runtime.GOOS == "linux" {
        host = 2
        fd5, err = os.Open("/sys/class/gpio/gpio5/value")
        if err != nil {
            fmt.Println(err)
            return
        }
        fd6, _ = os.Open("/sys/class/gpio/gpio6/value")
        fd13, _ = os.Open("/sys/class/gpio/gpio13/value")
        fd19, _ = os.Open("/sys/class/gpio/gpio19/value")
        fd26, _ = os.Open("/sys/class/gpio/gpio26/value")
        fd21, _ = os.Open("/sys/class/gpio/gpio21/value")
        fd20, _ = os.Open("/sys/class/gpio/gpio20/value")
        buf = make([]byte, 1)
    } else {
        fmt.Printf("No control box support for %s\n", runtime.GOOS)
    }

    curstate := 0
    filterset := 0
    filtercnt := 0

    // 等待其他 goroutines 初始化完成
    time.Sleep(100 * time.Millisecond)

    for {
        time.Sleep(10 * time.Millisecond) // 每 10 毫秒检查一次状态

        newstate := 0
        if host == 1 { // Plan 9 系统下的处理
            n, err := fdg.ReadAt(buf, 0)
            if n != 16 {
                fmt.Print(err)
            }
            s := string(buf)
            x, _ := strconv.ParseUint(s, 16, 64)
            if x&(1<<6) == 0 {
                newstate |= 0x01
            }
            if x&(1<<5) == 0 {
                newstate |= 0x02
            }
            if x&(1<<21) == 0 {
                newstate |= 0x04
            }
            if x&(1<<20) == 0 {
                newstate |= 0x08
            }
            if x&(1<<26) == 0 {
                newstate |= 0x10
            }
            if x&(1<<19) == 0 {
                newstate |= 0x20
            }
            if x&(1<<13) == 0 {
                newstate |= 0x40
            }
        } else if host == 2 { // Linux 系统下的处理
            n, err := fd5.ReadAt(buf, 0)
            if n != 1 {
                fmt.Println(err)
            }
            if buf[0] == '0' {
                newstate |= 0x02
            }
            fd6.ReadAt(buf, 0)
            if buf[0] == '0' {
                newstate |= 0x01
            }
            fd13.ReadAt(buf, 0)
            if buf[0] == '0' {
                newstate |= 0x40
            }
            fd19.ReadAt(buf, 0)
            if buf[0] == '0' {
                newstate |= 0x20
            }
            fd26.ReadAt(buf, 0)
            if buf[0] == '0' {
                newstate |= 0x10
            }
            fd21.ReadAt(buf, 0)
            if buf[0] == '0' {
                newstate |= 0x04
            }
            fd20.ReadAt(buf, 0)
            if buf[0] == '0' {
                newstate |= 0x08
            }
        }

        // 处理状态变化
        if newstate != filterset || newstate&0x70 == 0 {
            filtercnt = 0
            filterset = newstate
        } else {
            filtercnt++
        }

        if filtercnt == 4 {
            if newstate != curstate {
                diff := newstate ^ curstate
                if diff&0x70 != 0 {
                    switch newstate & 0x70 {
                    case 0x10:
                        proccmd("s cy.op 1a")
                    case 0x20:
                        proccmd("s cy.op 1p")
                    case 0x60:
                        proccmd("s cy.op co")
                    }
                }
                if diff&0x01 != 0 && newstate&0x01 != 0 {
                    proccmd("b c")
                }
                if diff&0x02 != 0 && newstate&0x02 != 0 {
                    proccmd("b r")
                }
                if diff&0x04 != 0 && newstate&0x04 != 0 {
                    proccmd("b i")
                }
                if diff&0x08 != 0 && newstate&0x08 != 0 {
                    proccmd("b p")
                }
                curstate = newstate
            }
        }
    }
}

/** 使用 flag 包解析命令行选项：
	-c: 使用便携式控制站连接到 GPIO 引脚。
	-D: 自动循环切换视角。
	-g: 不运行图形用户界面（GUI）。
	-K: 绕过某些内存泄漏问题。
	-L: 使用外部 LED 矩阵驱动器。
	-w: 模拟窗口的宽度（像素）
**/

func main() {
    // 定义通道数组，用于处理不同的脉冲信号
    var acccyc [20]chan pulse
    var ftcyc [3]chan pulse

    // 设置 flag.Usage 以提供帮助信息
    flag.Usage = func() {
        fmt.Fprintf(os.Stderr, "Usage: %s [options] [configuration file]\n", os.Args[0])
        flag.PrintDefaults()
    }

    // 解析命令行标志
    usecontrol = flag.Bool("c", false, "use a portable control station connected to GPIO pins")
    demomode = flag.Bool("D", false, "automatically cycle among perspectives")
    nogui := flag.Bool("g", false, "run without GUI")
    tkkludge = flag.Bool("K", false, "work around wish memory leaks")
    useled := flag.Bool("L", false, "use an external LED matrix driver")
    wp := flag.Int("w", 0, "`width` of the simulation window in pixels")

    flag.Parse()
    width = *wp
    guistate.proc = nil

    // 如果没有指定 -g 标志，则启动 GUI 协程
    if !*nogui {
        go gui()
        ppunch = make(chan string)
    }

    // 如果指定了 -c 标志，则启动控制站协程
    if *usecontrol {
        go ctlstation()
    }

    // 如果指定了 -L 标志，则启动 LED 显示协程
    if *useled {
        go leddisplay()
    }

    // 初始化各种通道
    initbut = make(chan int)
    cycsw = make(chan [2]string)
    cycbut = make(chan int)
    mpsw = make(chan [2]string)
    divsw = make(chan [2]string)
    multsw = make(chan [2]string)
    conssw = make(chan [2]string)
    cycout := make(chan pulse)
    cyctrunk := make([]chan pulse, 0, 40)

    // 创建初始脉冲通道
    initcyc = make(chan pulse)
    mpcyc = make(chan pulse)
    divcyc = make(chan pulse)
    multcyc = make(chan pulse)
    conscyc = make(chan pulse)
    prsw = make(chan [2]string)

    // 将初始脉冲通道添加到 cyctrunk 列表中
    p := append(cyctrunk, initcyc, mpcyc, divcyc, multcyc, conscyc)

    // 初始化累加器相关的通道
    for i := 0; i < 20; i++ {
        accsw[i] = make(chan [2]string)
        acccyc[i] = make(chan pulse)
        p = append(p, acccyc[i])
    }

    // 初始化功能测试相关的通道
    for i := 0; i < 3; i++ {
        ftsw[i] = make(chan [2]string)
        ftcyc[i] = make(chan pulse)
        p = append(p, ftcyc[i])
    }

    // 启动 fanout 协程，用于将脉冲分发到多个输出通道
    go fanout(cycout, p)

    // 启动各个控制器协程
    go consctl(conssw)
    go mpctl(mpsw)
    go cyclectl(cycsw)
    go divsrctl(divsw)
    go multctl(multsw)
    go prctl(prsw)

    // 启动初始单元和各功能单元协程
    go initiateunit(initcyc, initbut)
    go mpunit(mpcyc)
    go cycleunit(cycout, cycbut)
    go divunit(divcyc)
    go multunit(multcyc)
    go consunit(conscyc)

    // 启动累加器相关协程
    for i := 0; i < 20; i++ {
        go accctl(i, accsw[i])
        go accunit(i, acccyc[i])
    }

    // 启动功能测试相关协程
    for i := 0; i < 3; i++ {
        go ftctl(i, ftsw[i])
        go ftunit(i, ftcyc[i])
    }

    // 如果提供了配置文件参数，则加载配置文件
    if flag.NArg() >= 1 {
        // 等待其他 goroutines 初始化完成
        time.Sleep(100 * time.Millisecond)
        proccmd("l " + flag.Arg(0))
    }

    // 从标准输入读取命令并执行
    sc := bufio.NewScanner(os.Stdin)
    fmt.Print("> ")
    for sc.Scan() {
        if proccmd(sc.Text()) < 0 {
            break
        }
        fmt.Print("> ")
    }
}
